package com.ibix.chj;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ibix.chj.autohome.AutoHomePageProcessor;
import com.ibix.chj.entity.TblCarCategory;
import com.ibix.chj.model.CarBrand;
import com.ibix.chj.model.CarListByCharacter;
import com.ibix.chj.model.CarModel;
import com.ibix.chj.model.CarSeries;

import us.codecraft.webmagic.Spider;

public class ChjSpider {

	private static String currentPath;
	
	public static void main(String[] args) {
		args = new String[]{"-e"};
		
		long millis = System.currentTimeMillis();
		currentPath = getPath();

		if (args != null && hasArgs(args, "-e"))
		{
			try {
				String resultStr = getStringFromFile(currentPath + "/data.json");
				List<TblCarCategory> list = getListFromJson((JSONObject) JSONObject.parse(resultStr));
				initDB();
				updateDB(list);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				releaseDB();
			}
			return;
		}
		
//		args = new String[]{"A"};
		
		AutoHomePageProcessor ahpp = new AutoHomePageProcessor(args);

		String url = "http://car.autohome.com.cn/AsLeftMenu/As_LeftListNew.ashx?typeId=1&brandId=0&fctId=0&seriesId=0";
		Spider spider = Spider.create(ahpp);
		spider.addUrl(url);
		spider.thread(1);
		spider.run();

		String str = JSON.toJSONString(ahpp.getDataMap());

		if (compareWithLocalVersion(str)) {
			str = updateData(str);
			updateVersionTxt();
			writeStringToFile(str, currentPath + "/data.json");
			String strLite = makeDateLite(str);
			writeStringToFile(strLite, currentPath + "/dataLite.json");
		} else {
			System.out.println("数据一致，无需更新");
		}
		System.out.println("总共耗时：" + (System.currentTimeMillis() - millis) / 1000 + "秒");

		try {
			initDB();

			List<TblCarCategory> list = new ArrayList<TblCarCategory>();
			
			ConcurrentHashMap<String, CarListByCharacter> dataMap = ahpp.getDataMap();
			Enumeration<String> enumeration = dataMap.keys();
			
			while(enumeration.hasMoreElements()) {
				String key = enumeration.nextElement();
				
				CarListByCharacter clc = dataMap.get(key);
				
				ArrayList<CarBrand> brands = clc.getBrands();
				
				for (CarBrand cb : brands) {
					ArrayList<CarSeries> series = cb.getSeries();
					for(CarSeries cs : series) {
						ArrayList<CarModel> models = cs.getModels();
						for(CarModel cm : models) {
							TblCarCategory tcc = new TblCarCategory();
							tcc.setId(cm.getId());
							tcc.setCasename(clc.getCharacter().trim());
							tcc.setBrandname(cb.getName().trim());
							tcc.setSeriesname(cs.getName().trim());
							tcc.setModelname(cm.getName().trim());
//							tcc.setIcon(cb.getIcon());
							tcc.setCategory(cs.getCategory());
							tcc.setSecondcate(cs.getSecondaryCategory());
							tcc.setBeingsold((byte) (cm.getBeingSold()?1:0));
							
							String priceStr = cm.getGuidePrice().replaceAll("万", "");
							try {
								float price = Float.parseFloat(priceStr);
								tcc.setPrice(price);
							} catch (Exception e) {
								tcc.setPrice(0f);
							}
							
							list.add(tcc);
						}
					}
				}
			}
			
			updateDB(list);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			releaseDB();
		}
	}
	
	private static boolean hasArgs(String[] args,String string) {
		for (String arg : args) {
			if (arg.equals(string))
				return true;
		}
		return false;
	}

	public static List<TblCarCategory> getListFromJson(JSONObject json)
	{
		ConcurrentHashMap<Long, Object> map = new ConcurrentHashMap<Long, Object>();
		List<TblCarCategory> list = new ArrayList<TblCarCategory>();
		Iterator<String> iterator = json.keySet().iterator();
		
		int index = 0;
		while(iterator.hasNext())
		{
			String key = iterator.next();
			JSONObject characterData = json.getJSONObject(key);
			String character = characterData.getString("character");
			
			JSONArray brandsArray = characterData.getJSONArray("brands");
			for (int i = 0, length = brandsArray.size(); i < length; i++) {
				JSONObject brand = brandsArray.getJSONObject(i);
				String brandName = brand.getString("name");
				
				JSONArray seriesArray = brand.getJSONArray("series");
				for (int j = 0, length2 = seriesArray.size(); j < length2; j++) {
					JSONObject series = seriesArray.getJSONObject(j);
					String seriesName = series.getString("name");
					String category = series.getString("category");
					String secondaryCategory = series.getString("secondaryCategory");
					
					JSONArray modelsArray = series.getJSONArray("models");
					for (int k = 0, length3 = modelsArray.size(); k < length3; k++) {
						JSONObject model = modelsArray.getJSONObject(k);
						
						TblCarCategory tcc = new TblCarCategory();
						tcc.setId(model.getLong("id"));
						tcc.setCasename(character);
						tcc.setBrandname(brandName);
						tcc.setSeriesname(seriesName);
						tcc.setModelname(model.getString("name"));
//						tcc.setIcon(cb.getIcon());
						tcc.setCategory(category);
						tcc.setSecondcate(secondaryCategory);
						tcc.setBeingsold((byte) (model.getBoolean("beingSold")?1:0));
						
						String priceStr = model.getString("guidePrice").replaceAll("万", "");
						try {
							float price = Float.parseFloat(priceStr);
							tcc.setPrice(price);
						} catch (Exception e) {
							tcc.setPrice(0f);
						}
						
						if (map.containsKey(tcc.getId()))
						{
							System.out.println("No."+ index++ + "重复的车辆数据：  [" + map.get(tcc.getId()) + "]   [" + tcc + "]");
							continue;
						}
						else {
							map.put(tcc.getId(), tcc);
						}
						
						list.add(tcc);
					}
				}
			}
		}
		
		System.out.println("车型总数:" + list.size());
		return list;
	}

	public static String getPath() {
		File f = new File("");
		String path = f.getAbsolutePath();
		File targetPath = new File(path + "/ah");
		if (!targetPath.exists())
			targetPath.mkdirs();
		return targetPath.getAbsolutePath();
	}

	private static String updateData(String newJsonString) {
		String localJsonString = getStringFromFile(currentPath + "/data.json");
		if (localJsonString == null) {
			return newJsonString;
		}
		JSONObject newObj = JSONObject.parseObject(newJsonString);
		JSONObject localObj = JSONObject.parseObject(localJsonString);

		Iterator<String> newObjKeys = newObj.keySet().iterator();

		while (newObjKeys.hasNext()) {
			String key1 = newObjKeys.next();
			localObj.put(key1, newObj.get(key1));
		}

		return localObj.toJSONString();
	}

	private static void updateVersionTxt() {
		String versionStr = getStringFromFile(currentPath + "/version.json");
		if (versionStr == null) {
			versionStr = "{\"version\":1}";
		} else {
			JSONObject versionObj = JSONObject.parseObject(versionStr);

			int version = versionObj.getIntValue("version");
			version++;
			versionObj.put("version", version);
			versionStr = versionObj.toJSONString();
		}
		System.out.println("数据版本号更新至：" + versionStr);
		writeStringToFile(versionStr, currentPath + "/version.json");
	}

	private static String getStringFromFile(String path) {
		try {
			FileInputStream fis = new FileInputStream(path);
			byte[] buf = new byte[fis.available()];
			fis.read(buf);
			fis.close();
			return new String(buf, "UTF-8");
		} catch (FileNotFoundException e) {
			System.out.println(e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private static boolean compareWithLocalVersion(String str) {
		String localJsonString = getStringFromFile(currentPath + "/data.json");
		if (localJsonString == null) {
			return true;
		}
		JSONObject newObj = JSONObject.parseObject(localJsonString);
		JSONObject localObj = JSONObject.parseObject(str);
		if (!newObj.equals(localObj)) {
			return true;
		}
		return false;
	}

	private static String makeDateLite(String rawString) {
		JSONObject jsonObject = (JSONObject) JSON.parse(rawString);
		Iterator<String> it = jsonObject.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			JSONObject carListJson = jsonObject.getJSONObject(key);

			JSONArray brandArray = carListJson.getJSONArray("brands");
			int count = brandArray.size();
			for (int i = 0; i < count; i++) {
				JSONObject brandJson = brandArray.getJSONObject(i);

				brandJson.remove("icon");
				brandJson.remove("link");

				JSONArray seriesArray = brandJson.getJSONArray("series");
				int seriesCount = seriesArray.size();
				for (int j = 0; j < seriesCount; j++) {
					JSONObject seriesJson = seriesArray.getJSONObject(j);

					seriesJson.remove("link");

					JSONArray modelsArray = seriesJson.getJSONArray("models");
					int modelsCount = modelsArray.size();
					for (int k = 0; k < modelsCount; k++) {
						JSONObject modelJson = modelsArray.getJSONObject(k);
						modelJson.remove("guidePrice");
					}
				}
			}
		}
		return jsonObject.toJSONString();
	}

	private static void writeStringToFile(String str, String filePath) {
		try {
			byte[] bytes = str.getBytes("UTF-8");
			File file = new File(filePath);
			if (!file.exists()) {
				file.createNewFile();
			}
			System.out.println("输出文件至:" + file.getAbsolutePath());
			FileOutputStream fos = new FileOutputStream(file);
			DataOutputStream dos = new DataOutputStream(fos);
			int totalLength = bytes.length;
			int blockLength = 4096;
			int offset = 0;
			while (offset < totalLength) {
				int writeLength = blockLength;
				if (offset + writeLength >= totalLength) {
					writeLength = totalLength - offset;
				}
				dos.write(bytes, offset, writeLength);
				offset += blockLength;
			}
			dos.flush();
			dos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static SessionFactory sessionFactory;

	private static void initDB() throws Exception {
		// A SessionFactory is set up once for an application!
		final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure() // configures
																									// settings
																									// from
																									// hibernate.cfg.xml
				.build();
		try {
			sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
		} catch (Exception e) {
			// The registry would be destroyed by the SessionFactory, but we had
			// trouble building the SessionFactory
			// so destroy it manually.
			StandardServiceRegistryBuilder.destroy(registry);

			e.printStackTrace();
		}
	}

	private static void releaseDB() {
		if (sessionFactory != null) {
			sessionFactory.close();
		}
	}

	public static void updateDB(List<TblCarCategory> list) {
		Session session = sessionFactory.openSession();
//		session.beginTransaction();
//
//		String hqlUpdate = "delete TblCarCategory";
//
//		int deleteCount = session.createQuery(hqlUpdate).executeUpdate();
//
//		System.out.println("deleteCount=" + deleteCount);
//
//		session.getTransaction().commit();
//		session.close();

		// create a couple of events...
		session = sessionFactory.openSession();
		session.beginTransaction();

		int i = 0;
		System.out.println("addCount=" + list.size());
		
		StringBuilder sb = new StringBuilder();
		String nowStr = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
		
		for (TblCarCategory t : list) {

			if (t.getPrice() == 0)
			{
				sb.append(nowStr).append("   ***抓取车型价格失败:").append(t.toString()).append("\n");
			}
			
			if (t.getId() == 0) {
				sb.append(nowStr).append("   ###抓取车型数据失败:").append(t.toString()).append("\n");
				continue;
			}
			
			try {
//				session.merge(t);
				session.saveOrUpdate(t);
			} catch (Exception e)
			{
				e.printStackTrace();
				continue;
			}

			if (i++ % 1000 == 0) {
				session.flush();
				session.clear();
			}
		}
		
		session.getTransaction().commit();
		session.close();

		// now lets pull events from the database and list them
		// session = sessionFactory.openSession();
		// session.beginTransaction();
		// List result = session.createQuery( "from Event" ).list();
		// for ( Event event : (List<Event>) result ) {
		// System.out.println( "Event (" + event.getDate() + ") : " +
		// event.getTitle() );
		// }
		// session.getTransaction().commit();
		// session.close();
		
		String log = sb.toString();
		if (log.length() > 0)
			writeStringToFile(log, currentPath + "/log.txt");
	}
}
